import logging
from blls.abstract_base_bll import AbstractBaseBLL
from dtos.result import Result
from utils.guid_util import GUIDUtil
from utils.common_util import CommonUtil
from libs.session_wrapper import SessionWrapper
from enumobjects.general_enum import YesOrNoEnum
from sqlalchemy import or_
from config import HTTP_FILE_URL,REMOTE


class BaseBLL(AbstractBaseBLL):

    def _init_data(self, kwargs):
        """
        初始化创建对象时的数据
        :param kwargs:
        :return:
        """
        timestamp = CommonUtil.get_timestamp()
        current_user_obj = self.get_current_user()
        guid = str(GUIDUtil.generate())
        kwargs['guid'] = guid
        kwargs['create_user_id'] = current_user_obj.id
        kwargs['last_update_user_id'] = current_user_obj.id
        kwargs['create_time'] = timestamp
        kwargs['last_update_time'] = timestamp
        return kwargs

    def _get_object(self, kwargs):
        """
        获取当前类的实例化对象
        :param kwargs:
        :return:
        """
        return self.cls(**kwargs) if self.cls else None

    @classmethod
    def table_exists(cls, session, table_name):
        """
        检查指定表名是否存在与数据库中
        :param session:
        :param table_name:
        :return:
        """
        tables = session.execute("show tables;").fetchall()
        tables = [item[0] if item else None for item in tables]
        return table_name in tables

    @classmethod
    def check_query_result_is_none(cls, query_result_list):
        for item in query_result_list:
            obj, field = item
            if not obj:
                return Result.error_result(message="没有根据传递的%s找到对应的数据" % field)
        return Result.success_result()

    @classmethod
    def check_query_result_count(cls, query_result_list):
        for item in query_result_list:
            obj_list, params_list, field = item
            if len(obj_list) != len(params_list):
                return Result.error_result(message="参数%s中存在错误，请检查" % field)
        return Result.success_result()

    def get_user_id_dict(self, user_id_list):
        from models.user_models.system_user_model import SystemUsers
        user_res = self.db_session.query(SystemUsers.id, SystemUsers.name, SystemUsers.username,
                                         SystemUsers.mail, SystemUsers.phone).filter(
            SystemUsers.is_delete == YesOrNoEnum.NO.value,
            SystemUsers.id.in_(user_id_list)
        ).all()
        user_res_dict = {
            item[0]: {
                "name": item[1],
                "username": item[2],
                "mail": item[3],
                "phone": item[4],
            } for item in user_res
        }
        return user_res_dict

    def get_current_user(self):
        username = self.request.get_current_username()
        if not username:
            return None
        from models.user_models.system_user_model import SystemUsers
        user_obj = self.db_session.query(SystemUsers).filter(
            SystemUsers.is_delete == YesOrNoEnum.NO.value,
            SystemUsers.username == username
        ).first()
        return user_obj

    def get_current_user_id(self):
        username = self.request.get_current_username()
        if not username:
            return None
        from models.user_models.system_user_model import SystemUsers
        user_obj = self.db_session.query(SystemUsers.id).filter(
            SystemUsers.is_delete == YesOrNoEnum.NO.value,
            SystemUsers.username == username
        ).first()
        return user_obj[0] if user_obj else None

    def get_one_obj_by_field(self, field, value, cls=None, session=None, delete=True):
        """
        通过指定字段与值查找出对应的对象（单个）
        :param field:
        :param value:
        :param cls:
        :param session:
        :param delete:
        :return:
        """
        cls = cls if cls else self.cls
        session = session if session else self.db_session
        res = session.query(cls).filter(
            getattr(cls, field) == value
        )
        if delete:
            res = res.filter(
                getattr(cls, 'is_delete') == YesOrNoEnum.NO.value
            )
        res = res.first()
        return res

    def get_all_obj_by_field(self, field, value: list, cls=None, session=None, delete=True):
        """
        通过指定字段与值查找出对应的对象（多个）
        :param field:
        :param value:
        :param cls:
        :param session:
        :return:
        """
        cls = cls if cls else self.cls
        session = session if session else self.db_session
        res = session.query(cls).filter(
            getattr(cls, field).in_(value)
        )
        if delete:
            res = res.filter(
                getattr(cls, 'is_delete') == YesOrNoEnum.NO.value
            )
        res = res.all()
        return res

    def _unique_check(self, model, edit_params=None, guid=None, session=None):
        """
        唯一性检查，编辑时的检查会排除掉自身
        :param model:
        :param edit_params:
        :param guid:
        :param session:
        :return:
        """
        if not self.unique_fields:
            return Result.success_result()
        for unique_field in self.unique_fields:
            value = getattr(model, unique_field) if not edit_params else edit_params.get(unique_field, None)
            if not value and not guid:
                return Result.error_result(message="unique_fields中参数名配置错误")
            _check_obj = self.get_one_obj_by_field(unique_field, value, session=session)
            m_guid = model.guid if not guid else guid
            if _check_obj and _check_obj.guid != m_guid:
                return Result.error_result(message="%s值为%s的数据已经存在" % (unique_field, value))
        return Result.success_result()

    def _joint_cls_params(self, kwargs):
        cls_params = {}
        for k in kwargs.keys():
            cls_params[getattr(self.cls, k)] = kwargs[k]
        return cls_params

    def join_edit(self, guid: str, **kwargs):
        """
        编辑bll，根据对象的guid进行编辑，编辑时需要传递所有参数
        :param guid:
        :param kwargs:
        :return:
        """
        model = self.get_one_obj_by_field("guid", guid)
        if not model:
            return Result.error_result(message="没有根据guid找到对应对象")
        unique_check = self._unique_check(model, kwargs, guid)
        if not unique_check.is_success:
            return unique_check
        self._last_update_info(kwargs)
        res = SessionWrapper.update(self.cls, self.db_session, guid, kwargs)
        if res.is_success:
            res.data = {
                "guid": model.guid
            }
        return res

    def create(self, **kwargs):
        """
        新增bll
        :param kwargs:
        :return:
        """
        self._init_data(kwargs)
        try:
            model = self._get_object(kwargs)
            if not model:
                return Result.error_result(message="初始化BLL时，未设置cls参数")
        except Exception as ex:
            return Result.error_result(message="初始化模型时出现错误，原因：%s" % str(ex))
        unique_check = self._unique_check(model)
        if not unique_check.is_success:
            return unique_check
        res = SessionWrapper.add(self.db_session, model)
        if res.is_success:
            res.data = {
                "guid": model.guid
            }
        return res

    def _last_update_info(self, data: dict):
        data["last_update_user_id"] = self.get_current_user_id()
        data["last_update_time"] = CommonUtil.get_timestamp()

    def batch_delete(self, guid_list: list, is_delete: int):
        """
        批量删除/恢复bll
        :param guid_list:
        :param is_delete:
        :return:
        """
        message = "%s成功" % ("删除" if is_delete else "恢复")
        conditions = (getattr(self.cls, "guid").in_(guid_list),
                      getattr(self.cls, "is_delete") == (YesOrNoEnum.NO.value if is_delete else YesOrNoEnum.YES.value))
        kwargs = {
            "is_delete": YesOrNoEnum.YES.value if is_delete else YesOrNoEnum.NO.value,
        }
        res = self.db_session.query(self.cls).filter(
            *conditions
        ).all()
        current_user_id = self.get_current_user_id()
        self.db_session.begin(subtransactions=True)
        try:
            for r in res:
                r.is_delete = kwargs['is_delete']
                r.last_update_user_id = current_user_id
                r.last_update_time = CommonUtil.get_timestamp()
            self.db_session.commit()
            return Result.success_result(message=message)
        except Exception as ex:
            logging.info("批量删除时出现异常，原因：%s" % str(ex))
            self.db_session.rollback()
            return Result.error_result()

    @staticmethod
    def equals_filter(cls, field, value):
        return getattr(cls, field) == value

    @staticmethod
    def unequals_filter(cls, field, value):
        return getattr(cls, field) != value

    @staticmethod
    def like_filter(cls, field, value):
        return getattr(cls, field).like(f'%{value}%')

    @staticmethod
    def in_filter(cls, field, value):
        return getattr(cls, field).in_(value)

    @staticmethod
    def not_in_filter(cls, field, value):
        return getattr(cls, field).notin_(value)

    @staticmethod
    def greater_than(cls, field, value):
        return getattr(cls, field) > value

    @staticmethod
    def greater_or_equal(cls, field, value):
        return getattr(cls, field) >= value

    @staticmethod
    def less_than(cls, field, value):
        return getattr(cls, field) < value

    @staticmethod
    def less_or_equal(cls, field, value):
        return getattr(cls, field) <= value

    @property
    def filter_func_mode(self):
        return {
            "=": self.equals_filter,
            "!=": self.unequals_filter,
            "like": self.like_filter,
            "in": self.in_filter,
            "not_in": self.not_in_filter,
            ">": self.greater_than,
            ">=": self.greater_or_equal,
            "<": self.less_than,
            "<=": self.less_or_equal,
            "*": None,
        }

    @property
    def _get_condition_fields(self):
        if hasattr(self.cls(), "get_condition_fields"):
            return self.cls().get_condition_fields
        return []

    @property
    def _join_condition_field(self):
        if hasattr(self.cls(), "join_condition_field"):
            return self.cls().join_condition_field
        return {}

    def _joint_query(self, query, join_type, condition):
        """
        对于查询语句的拼接
        :param query:
        :param join_type:
        :param condition:
        :return:
        """
        if not condition:
            return query
        filter_list = []
        for con in condition:
            if not con or con['method'] not in self.filter_func_mode.keys():
                continue
            field, value = con['field'], con['value']
            if field not in self._get_condition_fields:
                continue
            if "." not in field:
                filter_list.append(self.filter_func_mode[con['method']](self.cls, field, value))
                continue
            split_field = field.split(".")
            current_field, join_query_field = split_field[0], split_field[1]
            if current_field not in self._join_condition_field.keys():
                continue
            join_cls = self._join_condition_field[current_field].get("cls", None)
            if not join_cls:
                continue
            filter_list.append(self.filter_func_mode[con['method']](join_cls, join_query_field, value))
        query_filter = query.filter(or_(*filter_list)) if join_type == "or" else query.filter(*filter_list)
        return query_filter

    def _get_join_query(self, join_list):
        """
        获取关联后的查询
        :param join_list:
        :return:
        """
        if not join_list:
            return self.db_session.query(self.cls)
        cls_list = [self.cls]
        for join_item in join_list:
            join_cls = join_item['join_cls']
            cls_list.append(join_cls)
        query = self.db_session.query(*cls_list)
        for join_item in join_list:
            join_cls, join_field, method = join_item['join_cls'], join_item['field'], join_item.get('method', 'join')
            join_cls_primary_key = join_cls().get_primary_key if hasattr(join_cls, "get_primary_key") else "id"
            if method == 'join':
                query = query.join(join_cls, getattr(self.cls, join_field) == getattr(join_cls, join_cls_primary_key))
            else:
                query = query.outerjoin(join_cls,
                                        getattr(self.cls, join_field) == getattr(join_cls, join_cls_primary_key))
        return query

    @classmethod
    def common_query(cls, query_cls, query, page_num, page_size, order_by, order_type, require_count=True):
        query = query.order_by(getattr(query_cls, order_by).desc()) if order_type != "asc" else query.order_by(
            getattr(query_cls, order_by).asc())
        count = 0
        if require_count:
            count = query.count()
        query = query.limit(page_size).offset((page_num - 1) * page_size)
        return query, count

    @classmethod
    def get_query_and_count(cls, query, page_size, page_num):
        count = query.count()
        query = query.limit(page_size).offset((page_num - 1) * page_size)
        return query, count

    @classmethod
    def get_only_query(cls, query, page_size, page_num):
        query = query.limit(page_size).offset((page_num - 1) * page_size)
        return query

    def get_query(self, join_type, condition, page_num, page_size, order_by,
                  order_type, join_list=None,
                  has_is_delete=True, query=None, require_count=True):
        if not query:
            query = self._get_join_query(join_list)
            if has_is_delete:
                query = query.filter(getattr(self.cls, "is_delete") == YesOrNoEnum.NO.value)
            query = self._joint_query(query, join_type, condition)
        query = query.order_by(getattr(self.cls, order_by).desc()) if order_type != "asc" else query.order_by(
            getattr(self.cls, order_by).asc())
        if require_count:
            return self.get_query_and_count(query, page_size, page_num)
        return self.get_only_query(query, page_size, page_num)

    @classmethod
    def get_dict_by_obj(cls, query_fields, obj):
        d = {
            f: getattr(obj, f) for f in query_fields
        }
        return d

    def set_many_2_many_info(self, data_list, many_join_list):
        """
        设置多对多关系的数据结果集
        :param data_list:
        :param many_join_list:
        :return:
        """
        if not many_join_list:
            return
        """
        m2m_response_field字段用于对主表的约束，当查询出主表结果集后，并未返回真实的ID，仅返回了类似与guid的字段，需要设置一个这样的字段来约束中间表的结果集
        """
        unique_field = self.cls().m2m_response_field if hasattr(self.cls, "m2m_response_field") else "guid"
        unique_item_list = []
        for item in data_list:
            if unique_field in item.keys():
                unique_item_list.append(item[unique_field])
        for many_join_item in many_join_list:
            central_cls = many_join_item['central_cls']
            join_cls = many_join_item['join_cls']
            central_join_cls_id = many_join_item['central_join_cls_id']
            central_cls_id, query_fields = many_join_item['central_cls_id'], many_join_item['query_fields']
            show_field_name = many_join_item['show_field_name']
            join_res = self.db_session.query(getattr(self.cls, unique_field), join_cls).join(
                central_cls, getattr(central_cls, central_join_cls_id) == join_cls.id
            ).join(
                self.cls, self.cls.id == getattr(central_cls, central_cls_id)
            ).filter(
                getattr(self.cls, unique_field).in_(unique_item_list)
            ).all()
            join_dict = {}
            for join_r in join_res:
                k, v = join_r[0], join_r[1]
                if k not in join_dict.keys():
                    join_dict[k] = [self.get_dict_by_obj(query_fields, v)]
                    continue
                join_dict[k].append(self.get_dict_by_obj(query_fields, v))
            for data in data_list:
                join_k = data.get(unique_field, None)
                if not join_k:
                    continue
                join_data = join_dict.get(join_k, None)
                if join_data:
                    data[show_field_name] = join_data

    def query_list(self, conditions: dict, page_num: int, page_size: int, order_by: int, order_type: str,
                   join_list: list = None, many_join_list: list = None,
                   enum_list: list = None, has_is_delete: bool = True):
        """
        查询bll
        :param conditions: 条件
        :param page_num: 查询页
        :param page_size: 每页条数
        :param order_by: 排序字段
        :param order_type: 排序类型，desc/asc
        :param join_list: 关联字段集, 如：
                  [{
                     "join_cls": ChsiUsers,
                     "field": "create_user_id",
                     "query_fields": ["name", "username"]
                  }]
        :param many_join_list: 多对多关系的字段及，如：
                [{
                    "show_field_name": material_ids,
                    "join_cls": KsMaterial,
                    "central_join_cls_id": "material_id",
                    "central_cls_id": "advertisement_id",
                    "central_cls": KsAdvertisementMaterial,
                    "query_fields": ["guid", "name"]
                }]
        :param enum_list: 要展示的枚举值字段，如：
                [{
                    "field": "trade_type",
                    "enum_cls": TradeTypeEnum
                }]
        :param has_is_delete: 是否需要进行is_delete的筛选
        :return:
        """
        none_data = {
            "page_num": page_num,
            "page_size": page_size,
            "count": 0,
            "data_list": [],
        }
        try:
            join_type, condition = conditions['type'], conditions['data']
            primary_key = self.cls().get_primary_key if hasattr(self.cls, "get_primary_key") else "id"
            order_by = order_by if order_by in [col.name for col in self.cls.__table__.columns] else primary_key
            query, count = self.get_query(join_type, condition, page_num, page_size, order_by,
                                          order_type, join_list, has_is_delete)
            data = query.all()
            data_list = self.get_list_by_obj_list(data, join_list, enum_list)
            self.set_many_2_many_info(data_list, many_join_list)
            return {
                "page_num": page_num,
                "page_size": page_size,
                "count": count,
                "data_list": data_list,
            }
        except Exception as ex:
            logging.info("参数错误，原因：%s" % str(ex))
            return none_data

    @staticmethod
    def get_file_http_url(file_path):
        """
        文件 本地路径转为http链接
        """
        file_path = file_path.replace(f"{REMOTE}images", "")
        file_http_url = f"{HTTP_FILE_URL}{file_path}"
        return file_http_url